package net.lb.socketclient;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import net.lb.baseutil.StringUtil;
import net.lb.baseutil.collection.ByteBufferPool;
import net.lb.baseutil.encrypt.RSAUtil;
import net.lb.socketclient.filter.EndecrptFilter;
import net.lb.socketclient.listen.GetReadResult;
import net.lb.socketserver.bus.BusinessCommand;
import net.lb.socketserver.exception.ConnectErrorException;
import net.lb.socketserver.exception.LessThanProtocolException;
import net.lb.socketserver.exception.NotFitProtocolException;
import net.lb.socketserver.exception.NotIntactDataException;
import net.lb.socketserver.exception.TimeoutException;
import net.lb.socketserver.util.CheckReadBuffer;

public class AuthClientUtil extends ClientUtil
{
    private RSAUtil    rsaUtil;
    private ByteBuffer authBuffer = ByteBufferPool.getBuffer(1024);
    
    /**
     * 设置基本的加解密过滤器参数
     * 
     * @param endecrptFilter
     */
    public AuthClientUtil(EndecrptFilter endecrptFilter, byte[] privateKey, GetReadResult getReadResult)
    {
        super(getReadResult);
        this.endecrptFilter = endecrptFilter;
        rsaUtil = new RSAUtil();
        rsaUtil.setPrivateKey(privateKey);
    }
    
    @Override
    protected void getClientChannelInfo(boolean reuse) throws ConnectErrorException
    {
        if (reuse && clientChannelInfo != null && clientChannelInfo.expired() == false)
        {
            return;
        }
        if (clientChannelInfo != null)
        {
            clientChannelInfo.closeSocket();
        }
        boolean finishConnect = false;
        int tryTime = connectTrytimes;
        AsynchronousSocketChannel socketChannel = null;
        String address = null;
        do
        {
            try
            {
                socketChannel = AsynchronousSocketChannel.open(ChannelGroupSource.getChannelGroup());
                socketChannel.connect(new InetSocketAddress(ip, port)).get();
                address = socketChannel.getLocalAddress().toString() + "--->" + socketChannel.getRemoteAddress().toString();
                initAuth(socketChannel);
                finishConnect = true;
                break;
            }
            catch (Exception e)
            {
                try
                {
                    if (socketChannel != null)
                    {
                        socketChannel.close();
                    }
                }
                catch (Exception e1)
                {
                    ;
                }
                tryTime--;
            }
        } while (tryTime > 0);
        if (finishConnect == false)
        {
            throw new ConnectErrorException("加密客户端经过" + connectTrytimes + "次测试，无法完成连接");
        }
        clientChannelInfo = buildChannelInfo(address, socketChannel);
        clientChannelInfo.startReadWait();
    }
    
    /**
     * 初始化认证信息
     * 
     * @throws ExecutionException
     * @throws InterruptedException
     * @throws IOException
     * @throws ConnectErrorException
     * @throws TimeoutException
     */
    private void initAuth(AsynchronousSocketChannel socketChannel) throws InterruptedException, ExecutionException, IOException, ConnectErrorException, TimeoutException
    {
        authBuffer.clear();
        headFactory.putHeadInBuffer(authBuffer, BusinessCommand.AUTH, 0);
        authBuffer.put(BusinessCommand.AUTH).put(BusinessCommand.REQUEST_SUCCESS).putInt(0).flip();
        socketChannel.write(authBuffer).get();
        long tmpReadTiemout = readTimeout;
        authBuffer.clear();
        int length = -1;
        do
        {
            try
            {
                long t0 = System.currentTimeMillis();
                logger.debug("准备读取数据{}", authBuffer);
                socketChannel.read(authBuffer).get(tmpReadTiemout, TimeUnit.MILLISECONDS);
                tmpReadTiemout -= System.currentTimeMillis() - t0;
                length = CheckReadBuffer.checkReadBuffer(authBuffer, headFactory);
                if (length == authBuffer.position() - 10)
                {
                    break;
                }
            }
            catch (LessThanProtocolException e)
            {
                ;
            }
            catch (NotFitProtocolException e)
            {
                throw new ConnectErrorException(e);
            }
            catch (NotIntactDataException e)
            {
                authBuffer = ByteBufferPool.expandToSize(e.getNeedSize(), authBuffer);
            }
            catch (Exception e)
            {
                throw new ConnectErrorException(e);
            }
        } while (tmpReadTiemout > 0);
        if ((length + 10) != authBuffer.position())
        {
            logger.error("认证读取超时");
            throw new TimeoutException(readTimeout);
        }
        authBuffer.flip().position(6);
        byte[] key = new byte[authBuffer.getInt()];
        authBuffer.get(key);
        byte[] result = rsaUtil.decrypt(key);
        endecrptFilter.setKey(result);
        if (logger.isDebugEnabled())
        {
            logger.debug("从{}获取的密钥为{}", socketChannel.getRemoteAddress(), StringUtil.toHexString(result));
        }
    }
    
    /**
     * 关闭客户端，并且关闭连接。归还缓存资源
     */
    public void close()
    {
        super.close();
        ByteBufferPool.returnBuffer(authBuffer);
    }
    
}
